package org.luxor.commons.redis.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.luxor.commons.core.exception.BaseException;
import org.luxor.commons.core.utils.SystemTimeUtils;
import org.luxor.commons.core.web.RestStatus;
import org.luxor.commons.redis.annotation.LimitRate;
import org.luxor.commons.redis.client.RedisZSetClient;
import org.luxor.commons.redis.component.KeyGenerator;
import org.luxor.commons.redis.component.RedisKeys;
import org.luxor.commons.redis.component.ReflectUtil;

import java.util.concurrent.TimeUnit;

/**
 * 限制接口每秒流速的AOP实现类
 *
 * @author chenshun
 * @email sunlightcs@gmail.com
 * @date 2017-07-17 23:30
 */
@Aspect
public class LimitRateAspect {

    private RedisZSetClient redisClientZset;

    public void setRedisClientZset(RedisZSetClient redisClientZset) {
        this.redisClientZset = redisClientZset;
    }

    /**
     * 定义缓存逻辑
     */
    @Around("@annotation(org.luxor.commons.redis.annotation.LimitRate)")
    public Object limitRate(ProceedingJoinPoint pjp) throws Throwable {
        LimitRate limitRate = ReflectUtil.getMethod(pjp).getAnnotation(LimitRate.class);

        String limitRateKey = RedisKeys.builder("limitRate").append(KeyGenerator.INSTANCE.getlimitRateKey(pjp, limitRate)).build();
        long currentTime = SystemTimeUtils.now();

        if (redisClientZset.hasKey(limitRateKey)) {
            // 调用次数 = ( ( 当前时间 - 间隔时间 ) -> 当前时间 ) 内的总次数
            int count = redisClientZset.zRangeByScore(limitRateKey, currentTime - limitRate.intervalTime(), currentTime).size();
            // 如果超出流量限制
            if (count > limitRate.rate()) {
                throw new BaseException(RestStatus.LIMIT_RATE_ERROR, "操作过于频繁(限制" + limitRate.rate() + " reqs/s)，请求稍后再试.");
            }
            // 移除历史流量统计记录
            long startRemoveTime = currentTime - limitRate.intervalTime() * 2L;
            long endRemoveTIme = currentTime - limitRate.intervalTime();
            redisClientZset.zRemoveRange(limitRateKey, startRemoveTime, endRemoveTIme);
        }

        // 添加流量统计记录
        redisClientZset.zAdd(limitRateKey, Long.toString(currentTime), currentTime);

        // 延长过期时间
        redisClientZset.expire(limitRateKey, limitRate.intervalTime(), TimeUnit.MILLISECONDS);

        return pjp.proceed();
    }

}